/*
 * @文件描述: 全局变量hook
 * @公司: 山东大学信息学院
 * @作者: 李洪文
 * @Date: 2020-09-02 11:54:59
 * @LastEditors: 李洪文
 * @LastEditTime: 2020-10-10 11:01:27
 */
import {useEffect, useState} from 'react';

type DispatcherFunctionType = (value: any) => void;
type DispatcherType = null | DispatcherFunctionType;

export interface KeysNodeType {
  value: any;
  dispatchers: Array<DispatcherType>;
  centralDispatcher: DispatcherFunctionType;
  [key: string]: string | Array<DispatcherType> | DispatcherFunctionType;
}
interface KeysType {
  [key: string]: KeysNodeType;
}
type UseGlobalStateReturnType = [any, DispatcherFunctionType];

const keys: KeysType = {},
  createCentralDispatcher = (key: string) => (value: any) => {
    keys[key].value = value;
    keys[key].dispatchers.forEach(
      (dispatcher: DispatcherType | null) => dispatcher && dispatcher(value),
    );
  };

function resetEmptyKeys(): void {
  Object.keys(keys).forEach((key: string) => {
    let allNull: boolean = true;
    keys[key].dispatchers.forEach((dispatcher: DispatcherType) => {
      if (!allNull) return;
      if (dispatcher !== null) {
        allNull = false;
      }
    });
    if (allNull) {
      keys[key].dispatchers = [];
    }
  });
}

function useGlobalState(
  key: string,
  initialState: any = '',
): UseGlobalStateReturnType {
  // Some basic cleaning
  resetEmptyKeys();

  const [state, dispatcher] = useState(initialState);
  if (!keys[key]) {
    keys[key] = {
      value: initialState,
      centralDispatcher: createCentralDispatcher(key),
      dispatchers: [],
    };
  }

  useEffect(() => {
    const index: number = keys[key].dispatchers.push(dispatcher) - 1;
    return () => {
      keys[key].dispatchers[index] = null;
    };
  }, []);

  const value: any = keys[key] && keys[key].value ? keys[key].value : state;
  return [value, keys[key].centralDispatcher];
}

export function useGlobal<T>(
  key: string,
  initialState: T,
): [T, (value: T) => void] {
  return useGlobalState(key, initialState);
}

export function getGlobal<T>(key: string, initialState: T): T {
  if (keys[key] && keys[key].value) {
    return keys[key].value;
  }
  return initialState;
}

export function setGlobal<T>(key: string, value: T) {
  if (!keys[key]) return;
  keys[key].value = value;
}
